from inventory.models import Material, Component
from django.core.exceptions import ObjectDoesNotExist
import csv

MATERIAL_SCHEME = (
    'chem.name',
    'chem.bottle_num',
    'chem.notes',
    'chem.ml_on_hand',
    'chem.substantivity',
    'chem.price_ml',
    )

COMPONENT_SCHEME = (
    'comp.accord_name',
    'comp.component_name',
    'comp.amount',
    'comp.dilution',
    )

def handle_import(data):
  reader = csv.reader(data)
  scheme = reader.next()
  if scheme[0] == MATERIAL_SCHEME[0]:
    return import_materials(reader, scheme)
  elif scheme[0] == COMPONENT_SCHEME[0]:
    return import_components(reader, scheme)


def import_materials(reader, scheme):
  """Import each row as a Material."""
  # Assume name is the first column
  name_ix = 0
  num_new = 0
  num_old = 0
  debug_msgs = []
  for d in reader:
    if len(d) < 2:
      continue
    else:
      name = d[name_ix]
      debug_msgs.append(name)
      is_new = False
      try:
        material = Material.objects.get(name=name)
        num_old += 1
      except ObjectDoesNotExist:
        is_new = True
        material = Material()
        material.name = name
        num_new += 1
      #except MultipleObjectsReturned:
        # FIXME: look for multiple objects w/ same name.
        #print 'Ignoring Multiple objects for name ', name
        #continue
    for ix in range(len(d)):
      if d[ix]:
        (class_name, attr) = scheme[ix].split('.')
        # TODO: catch attribute error
        if class_name == 'chem':
          material.__setattr__(attr, d[ix])
        elif class_name == 'bottle':
          # We used to distinguish between these classes, but not anymore.
          material.__setattr__(attr, d[ix])
    material.save()
  msg = 'Imported %d new materials and updated %d old ones.' % (num_new,
                                                                num_old)
  return (True, msg)


def import_components(reader, scheme):
  """Import each row as a Material."""
  errors = []
  num_imported = 0
  for d in reader:
    if len(d) < 2:
      continue
    (accord_name, component_name, amount, dilution) = d
    try:
      accord = Material.objects.get(name=accord_name)
    except ObjectDoesNotExist:
      # TODO maybe make and save new material?
      errors.append('Did not find material %s, skipping.' % accord_name)
      continue
    try:
      material = Material.objects.get(name=component_name)
    except ObjectDoesNotExist:
      # TODO maybe make and save new material?
      errors += 'Did not find material %s, skipping.' % component_name
      continue
    component = Component()
    # FIXME how to set reference? is this right?
    component.accord = accord
    component.component = material
    # Hooray for python type conversions?
    component.amount = amount
    component.dilution = dilution
    component.save()
    num_imported += 1
    if not accord.is_accord:
      accord.is_accord = True
      accord.save()
  msg = 'Imported %d components ' % num_imported
  if len(errors) == 0:
    msg += 'with no errors.'
  else:
    msg += 'with some errors:<br>\n%s' % '<br>\n'.join(errors)
  return (True, msg)


def handle_export(export_type, fp):
  if export_type == 'materials':
    return export_materials(fp)
  if export_type == 'components':
    return export_components(fp)
  else:
    return (False, 'I don\'t know how to export %s' % export_type)


def export_materials(fp):
  errors = ''
  fp.write(','.join(MATERIAL_SCHEME) + '\n')
  writer = csv.writer(fp)
  mats = Material.objects.all()
  for material in mats:
    data = [
             material.name,
             material.bottle_num,
             material.notes,
             material.ml_on_hand,
             material.substantivity,
             material.price_ml,
             ]
    writer.writerow(data)
  del writer
  success = True
  return (success, errors)


def export_components(fp):
  errors = ''
  fp.write(','.join(COMPONENT_SCHEME) + '\n')
  writer = csv.writer(fp)
  comps = Component.objects.all()
  for comp in comps:
    data = [ 
             comp.accord.name,
             comp.component.name,
             comp.amount,
             comp.dilution,
             ]
    writer.writerow(data)
  del writer
  success = True
  return (success, errors)

